#include "my_thread_mem.h"

#if (MY_THREAD_MEM_NO_POOL)

my_thread_mem_freelists_t *
my_thread_mem_freelists_create(int64_t water_level)
{
    (void)water_level;
    my_thread_mem_freelists_t *freelists;
    freelists = my_mem_calloc(sizeof(my_thread_mem_freelists_t));
    return freelists;
}


void *
my_thread_mem_freelists_alloc(my_thread_mem_freelists_t *freelists, size_t size)
{
    (void)freelists;
    return my_mem_alloc(size);
}


void
my_thread_mem_freelists_free(my_thread_mem_freelists_t *freelists, void *ptr)
{
    (void)freelists;
    my_mem_free(ptr);
}


void
my_thread_mem_freelists_destroy(my_thread_mem_freelists_t *freelists)
{
    my_mem_free(freelists);
}


my_thread_4kaligned_mem_freelists_t *
my_thread_4kaligned_mem_freelists_create(int64_t water_level)
{
    (void)water_level;
    my_thread_4kaligned_mem_freelists_t *freelists;
    freelists = my_mem_calloc(sizeof(my_thread_4kaligned_mem_freelists_t));
    return freelists;
}


void *
my_thread_4kaligned_mem_freelists_alloc(
    my_thread_4kaligned_mem_freelists_t *freelists, size_t size)
{
    (void)freelists;
    return my_4kaligned_mem_alloc(size);
}


void
my_thread_4kaligned_mem_freelists_free(
    my_thread_4kaligned_mem_freelists_t *freelists, void *ptr)
{
    (void)freelists;
    my_4kaligned_mem_free(ptr);
}


void
my_thread_4kaligned_mem_freelists_destroy(
    my_thread_4kaligned_mem_freelists_t *freelists)
{
    my_mem_free(freelists);
}


#else


my_thread_mem_freelists_t *
my_thread_mem_freelists_create(int64_t water_level)
{
    my_thread_mem_freelists_t *freelists;
    freelists = my_mem_calloc(sizeof(my_thread_mem_freelists_t));

    int                       i;

    for (i = 0; i < MEM_FREELIST_MODE_END; i++) {
        my_spinlock_init(&freelists->lists[i].lock);
        freelists->lists[i].space_size
            = my_mem_freelist_mode_to_size(i);
        freelists->lists[i].water_level = water_level;
        freelists->lists[i].count = 0;
        freelists->lists[i].used = 0;
        freelists->lists[i].head = (void *)(-1);
    }

    freelists->inited = MY_TRUE;    

    return freelists;
}


void *
my_thread_mem_freelists_alloc(my_thread_mem_freelists_t *freelists, size_t size)
{
    if (size > MY_MEM_MAX_ALLOC_SIZE) {
        assert(!"to big alloc size");
    }

    int                     i;
    int                     mode = -1;
    size_t                  mode_size;

    my_mem_item_head_t      *item = NULL;
    my_mem_freelist_t       *freelist = NULL;

    for (i = 0; i < MEM_FREELIST_MODE_END; i++) {
        mode_size = (size_t)my_mem_freelist_mode_to_size(i);
        if (size <= mode_size) {
            size = mode_size;
            mode = i;
            break;
        }
    }

    if (mode != -1 && freelists->inited) {
        freelist = &freelists->lists[mode];
    }

    if (freelist != NULL) {
        my_spinlock_lock(&freelist->lock);
        if (freelist->count > 0) {
            item = freelist->head;
            freelist->head = item->next;
            item->next = NULL;
            freelist->count--;
        }
        freelist->used++;
        my_spinlock_unlock(&freelist->lock);
    }

    if (item == NULL) {
        return my_mem_alloc(size);
    }

    return (void *)(item) + sizeof(my_mem_item_head_t);
}


void
my_thread_mem_freelists_free(my_thread_mem_freelists_t *freelists, void *ptr)
{
    my_mem_item_head_t      *item = NULL;
    my_mem_freelist_t       *freelist = NULL;
    int                     mode = -1;
    int                     i;

    item = ptr - sizeof(my_mem_item_head_t);
    assert(item->magic == MY_MEM_ITEM_MAGIC);
    if (item->next != NULL) {
        assert(!"double free or mem stampede");
    }

    for (i = 0; i < MEM_FREELIST_MODE_END; i++) {
        if (item->size == my_mem_freelist_mode_to_size(i)) {
            mode = i;
            break;
        }
    }

    if (mode != -1 && freelists->inited) {
        freelist = &freelists->lists[mode];
    }

    if (freelist != NULL) {
        my_spinlock_lock(&freelist->lock);
        if (freelist->count < freelist->water_level) {
            item->next = freelist->head;
            freelist->head = item;
            item = NULL;
            freelist->count++;
        }
        freelist->used--;
        my_spinlock_unlock(&freelist->lock);
    }

    if (item != NULL) {
        my_mem_free(ptr);
    }
}


static void
my_thread_mem_freelist_clean(my_mem_freelist_t *freelist)
{
    my_bool_t           exit = MY_FALSE;
    void                *ptr;
    my_mem_item_head_t  *item;

    while (1) {
        item = NULL;
        my_spinlock_lock(&freelist->lock);
        if (freelist->count > 0) {
            item = freelist->head;
            freelist->head = item->next;
            item->next = NULL;
            freelist->count--;
        } else {
            exit = MY_TRUE;
        }
        my_spinlock_unlock(&freelist->lock);

        if (item != NULL) {
            ptr = (void *)(item) + sizeof(my_mem_item_head_t);
            my_mem_free(ptr);
        }

        if (exit) {
            break;
        }
    }
}


void
my_thread_mem_freelists_destroy(my_thread_mem_freelists_t *freelists)
{
    int             i;

    if (freelists->inited) {
        for (i = 0; i < MEM_FREELIST_MODE_END; i++) {
            my_thread_mem_freelist_clean(&freelists->lists[i]);
        }
    }

    my_mem_free(freelists);
}


my_thread_4kaligned_mem_freelists_t *
my_thread_4kaligned_mem_freelists_create(int64_t water_level)
{
    my_thread_4kaligned_mem_freelists_t *freelists;
    freelists = my_mem_calloc(sizeof(my_thread_4kaligned_mem_freelists_t));

    int                                 i;
    for (i = 0; i < MEM_4KALIGNED_FREELIST_MODE_END; i++) {
        my_spinlock_init(&freelists->lists[i].lock);
        freelists->lists[i].space_size
            = my_4kaligned_mem_freelist_mode_to_size(i);
        freelists->lists[i].water_level = water_level;
        freelists->lists[i].count = 0;
        freelists->lists[i].used = 0;
        freelists->lists[i].head = (void *)(-1);
    }

    freelists->inited = MY_TRUE;

    return freelists;
}


void *
my_thread_4kaligned_mem_freelists_alloc(
    my_thread_4kaligned_mem_freelists_t *freelists, size_t size)
{
    if (size > MY_MEM_MAX_ALLOC_SIZE) {
        assert(!"to big alloc size");
    }

    my_mem_freelist_t           *freelist = NULL;
    my_aligned_mem_item_head_t  *item = NULL;
    int                         i;
    int                         mode = -1;
    size_t                      mode_size;

    for (i = 0; i < MEM_4KALIGNED_FREELIST_MODE_END; i++) {
        mode_size = (size_t)my_4kaligned_mem_freelist_mode_to_size(i);
        if (size <= mode_size) {
            size = mode_size;
            mode = i;
            break;
        }
    }

    if (mode != -1 && freelists->inited) {
        freelist = &freelists->lists[mode];
    }

    if (freelist != NULL) {
        my_spinlock_lock(&freelist->lock);
        if (freelist->count > 0) {
            item = freelist->head;
            freelist->head = item->next;
            item->next = NULL;
            freelist->count--;
        }
        freelist->used++;   
        my_spinlock_unlock(&freelist->lock);
    }

    if (item == NULL) {
        return my_4kaligned_mem_alloc(size);
    }

    return MY_4KALIGNED_MEM_ITEM_PTR(item);
}


void
my_thread_4kaligned_mem_freelists_free(
    my_thread_4kaligned_mem_freelists_t *freelists, void *ptr)
{
    void                                *p1;
    my_aligned_mem_item_head_t          *item;
    my_mem_freelist_t                   *freelist = NULL;
    int                                 mode = -1;
    int                                 i;

    p1 = ((void **)ptr)[-1];
    item = (my_aligned_mem_item_head_t *)p1;
    assert(item->magic == MY_ALIGNED_MEM_ITEM_MAGIC);
    if (item->next != NULL) {
        assert(!"double free or mem stampede");
    }

    for (i = 0; i < MEM_4KALIGNED_FREELIST_MODE_END; i++) {
        if (item->size == my_4kaligned_mem_freelist_mode_to_size(i)) {
            mode = i;
            break;
        }
    }

    if (mode != -1 && freelists->inited) {
        freelist = &freelists->lists[mode];
    }

    if (freelist != NULL) {
        my_spinlock_lock(&freelist->lock);
        if (freelist->count < freelist->water_level) {
            item->next = freelist->head;
            freelist->head = item;
            item = NULL;
            freelist->count++;
        }
        freelist->used--;
        my_spinlock_unlock(&freelist->lock);
    }

    if (item != NULL) {
        my_4kaligned_mem_free(ptr);
    }
}


static void
my_thread_4kaligned_mem_freelist_clean(my_mem_freelist_t *freelist)
{
    my_bool_t                   exit = MY_FALSE;
    void                        *ptr;
    my_aligned_mem_item_head_t  *item;

    while (1) {
        item = NULL;
        my_spinlock_lock(&freelist->lock);
        if (freelist->count > 0) {
            item = freelist->head;
            freelist->head = item->next;
            item->next = NULL;
            freelist->count--;
        } else {
            exit = MY_TRUE;
        }
        my_spinlock_unlock(&freelist->lock);

        if (item != NULL) {
            ptr = MY_4KALIGNED_MEM_ITEM_PTR(item);
            my_4kaligned_mem_free(ptr);
        }

        if (exit) {
            break;
        }
    }
}


void
my_thread_4kaligned_mem_freelists_destroy(
    my_thread_4kaligned_mem_freelists_t *freelists)
{
    int                 i;

    if (freelists->inited) {
        for (i = 0; i < MEM_4KALIGNED_FREELIST_MODE_END; i++) {
            my_thread_4kaligned_mem_freelist_clean(&freelists->lists[i]);
        }
    }

    my_mem_free(freelists);
}


#endif